using System;
using System.Collections.Generic;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
//using Microsoft.DirectX.Direct3DX;

namespace Verlet
{
    public enum ControlMode
    {
        Fixed, Free, FallAndStick
    }

    public sealed class Node
    {
        Vector3 m_position;
        Vector3 m_pposition;
        ControlMode m_mode = ControlMode.Free;
        public float staticFrictionThresholdSq = 1.0f;

        Vector3 m_acceleration = new Vector3(0f, Gravity, 0f);

        public static float Gravity = -32.0f;

        public static float airDamping = 0.1f;
        public static float groundDamping = 0.9f;

        public static float deltaT = 1 / 33.0f;
        public static float deltaTSq = 1 / (33.0f * 33.0f);
        static Random random = new Random();

        public static void SetUpdateInterval(float dt)
        {
            if (dt > 2.5f * deltaT)
                // Assume it's a transient and ignore it.
                return;

            // Mostly ignore the actual update inverval because Verlet
            // is unstable when the update rate changes.
            deltaT = 0.9f*deltaT+0.1f*dt;
            deltaTSq = deltaT*deltaT;
        }

        public Vector3 Acceleration
        {
            get
            {
                return m_acceleration;
            }
            set
            {
                m_acceleration = value;
            }
        }

        public Node()
        {
            m_position = new Vector3((float)random.NextDouble()-0.5f, (float)random.NextDouble()-0.5f, (float)random.NextDouble()-0.5f);
            m_pposition = m_position;
        }

        public Node(Vector3 position)
        {
            Position = position;
            m_pposition = position;
        }

        // Mutator enforces ground plane non-penetration.
        public Vector3 Position
        {
            get
            {
                return m_position;
            }
            set
            {
                if (m_mode!=ControlMode.Fixed && !(OnGround&&m_mode==ControlMode.FallAndStick))
                    m_position = new Vector3(value.X, Math.Max(0f, value.Y), value.Z);
            }
        }

        public Vector3 Velocity
        {
            get
            {
                return (m_position - m_pposition) * (1.0f/deltaT);
            }
            set
            {
                m_pposition = m_position - (value * deltaT);
            }
        }

        public Vector3 GroundVelocity
        {
            get
            {
                Vector3 v = Velocity;
                v.Y = 0;
                return v;
            }
            set
            {
                Vector3 v = m_position - (value * deltaT);
                m_pposition = new Vector3(v.X, m_pposition.Y, v.Z);
            }
        }

        public ControlMode ControlMode
        {
            get
            {
                return m_mode;
            }
            set
            {
                m_mode = value;
                if (value == ControlMode.Fixed)
                    m_pposition = m_position;
            }
        }

        public bool OnGround
        {
            get
            {
                return Position.Y <= 0;
            }
        }

        float Damping
        {
            get
            {
                return OnGround?groundDamping:airDamping;
            }
        }

        public void Update()
        {
            Vector3 save = Position;
            float damp = Damping;

            // Handle static friction when on the ground.
            if (OnGround && (save - m_pposition).LengthSq() < staticFrictionThresholdSq)
                m_pposition = save;

            Position = ((2.0f-damp)*save)-((1.0f-damp)*m_pposition)+(Acceleration*deltaTSq);
            m_pposition = save;
        }
    }
}